package cn.org.seventh.tools.plugin;

import java.util.Collections;
import java.util.Map;

import org.generama.FreeMarkerTemplateEngine;
import org.generama.JellyTemplateEngine;
import org.generama.QDoxCapableMetadataProvider;
import org.generama.WriterMapper;
import org.generama.defaults.QDoxPlugin;
import org.generama.defaults.XMLOutputValidator;

import com.thoughtworks.qdox.model.DocletTag;
import com.thoughtworks.qdox.model.JavaClass;
import com.thoughtworks.qdox.model.JavaMethod;

public class IbatisMapPlugin extends QDoxPlugin {
	private static final String DEFAULT_TYPE = "java.util.Map";
	private static final String XML_TYPE = "xml";
	private static final String OBJECT_TYPE = "java.lang.Object";

	// private String baseConfig;

	public IbatisMapPlugin(JellyTemplateEngine templateEngine,
			QDoxCapableMetadataProvider metadataProvider,
			WriterMapper writerMapper) {
		// super(new XmlFormatedFreeMarkerTemplateEngine(), metadataProvider,
		// writerMapper);
		super(new FreeMarkerTemplateEngine(), metadataProvider, writerMapper);
		setMultioutput(true);
		Map dtds = Collections.singletonMap(
				"http://ibatis.apache.org/dtd/sql-map-2.dtd", getClass()
						.getResource("sql-map-2.dtd"));
		setOutputValidator(new XMLOutputValidator(dtds));
		setValidate(false);
		new TagLibrary(metadataProvider);
	}

	public IbatisMapPlugin(FreeMarkerTemplateEngine templateEngine,
			QDoxCapableMetadataProvider metadataProvider,
			WriterMapper writerMapper) {
		// super(new XmlFormatedFreeMarkerTemplateEngine(), metadataProvider,
		// writerMapper);
		super(new FreeMarkerTemplateEngine(), metadataProvider, writerMapper);
		setMultioutput(true);
		Map dtds = Collections.singletonMap(
				"http://ibatis.apache.org/dtd/sql-map-2.dtd", getClass()
						.getResource("sql-map-2.dtd"));
		setOutputValidator(new XMLOutputValidator(dtds));
		setValidate(false);
		new TagLibrary(metadataProvider);
	}

	// public String getBaseConfig() {
	// String content = null;
	// if (baseConfig == null || "".equals(baseConfig)) {
	// throw new RuntimeException("base config must be define!");
	// }
	// try {
	// baseConfig = baseConfig.replace('\\', '/');
	// URL url = getClass().getResource(
	// baseConfig.startsWith("/") ? baseConfig : "/" + baseConfig);
	// File file = new File(url.getFile());
	// SAXReader reader = new SAXReader();
	// Document document = reader.read(file);
	// Element root = document.getRootElement();
	// ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
	// OutputFormat format = OutputFormat.createCompactFormat();
	// format.setSuppressDeclaration(true);
	// format.setEncoding("UTF-8");
	// format.setNewlines(true);
	// format.setIndent(true);
	// XMLWriter writer = new XMLWriter(out, format);
	// for (int i = 0; i < root.content().size(); i++) {
	// Node node = (Node) root.content().get(i);
	// writer.write(node);
	// }
	// content = new String(out.toByteArray(), "UTF-8");
	// writer.close();
	//
	// } catch (Exception e) {
	// throw new RuntimeException(e);
	// }
	// return content;
	// }

	// public void setBaseConfig(String baseConfig) {
	// this.baseConfig = baseConfig;
	// }

	public String getMap(Object metadata) {
		String id = getMapTagNamedParameter(metadata, "id",
				((JavaMethod) metadata).getName());
		String parameterClass = getMapTagNamedParameter(metadata,
				"parameter-class", DEFAULT_TYPE);
		String parameterMap = getMapTagNamedParameter(metadata,
				"parameter-map", null);
		String resultClass = getMapTagNamedParameter(metadata, "result-class",
				DEFAULT_TYPE);
		String resultMap = getMapTagNamedParameter(metadata, "result-map", null);
		String cacheModel = getMapTagNamedParameter(metadata, "cache-model",
				null);
		String fetchSize = getMapTagNamedParameter(metadata, "fetch-size", null);
		String remapResults = getMapTagNamedParameter(metadata,
				"remap-results", null);
		String resultSetType = getMapTagNamedParameter(metadata,
				"result-set-type", null);
		String timeout = getMapTagNamedParameter(metadata, "timeout", null);
		String xmlResultName = getMapTagNamedParameter(metadata,
				"xml-result-name", null);
		StringBuffer map = new StringBuffer("");
		map.append(" id=\"").append(id).append("\" ");
		if ("select".equals(getType(metadata))) {
			if (null != parameterMap) {
				map.append(" parameterMap=\"").append(parameterMap).append(
						"\" ");
			} else {
				map.append(" parameterClass=\"").append(parameterClass).append(
						"\" ");
			}
			if (null != resultMap) {
				map.append(" resultMap=\"").append(resultMap).append("\" ");
			} else {
				map.append(" resultClass=\"").append(resultClass).append("\" ");
				if (XML_TYPE.equals(resultClass) && null != xmlResultName) {
					map.append(" xmlResultName=\"").append(xmlResultName)
							.append("\" ");
				}
			}
			if (null != cacheModel) {
				map.append(" cacheModel=\"").append(cacheModel).append("\" ");
			}
			if (null != fetchSize) {
				try {
					Integer.parseInt(fetchSize);
					map.append(" fetchSize=\"").append(fetchSize).append("\" ");
				} catch (NumberFormatException e) {
					e.printStackTrace();
				}
			}
			if (null != remapResults) {
				if ("true".equals(remapResults.toLowerCase())
						|| "false".equals(remapResults.toLowerCase())) {
					map.append(" remapResults=\"").append(
							remapResults.toLowerCase()).append("\" ");
				}
			}
			if (null != resultSetType) {
				if ("SCROLL_SENSITIVE".equals(resultSetType.toUpperCase())
						|| "FORWARD_ONLY".equals(resultSetType.toUpperCase())
						|| "SCROLL_INSENSITIVE".equals(resultSetType
								.toUpperCase())) {
					map.append(" resultSetType=\"").append(
							resultSetType.toUpperCase()).append("\" ");
				}
			}
			if (null != timeout) {
				try {
					Integer.parseInt(timeout);
					map.append(" timeout=\"").append(timeout).append("\" ");
				} catch (NumberFormatException e) {
					e.printStackTrace();
				}
			}
		} else if ("insert".equals(getType(metadata))) {
			if (null != parameterMap) {
				map.append(" parameterMap=\"").append(parameterMap).append(
						"\" ");
			} else {
				map.append(" parameterClass=\"").append(parameterClass).append(
						"\" ");
			}
			if (null != timeout) {
				try {
					Integer.parseInt(timeout);
					map.append(" timeout=\"").append(timeout).append("\" ");
				} catch (NumberFormatException e) {
					e.printStackTrace();
				}
			}
		} else if ("delete".equals(getType(metadata))) {
			if (null != parameterMap) {
				map.append(" parameterMap=\"").append(parameterMap).append(
						"\" ");
			} else {
				map.append(" parameterClass=\"").append(parameterClass).append(
						"\" ");
			}
			if (null != timeout) {
				try {
					Integer.parseInt(timeout);
					map.append(" timeout=\"").append(timeout).append("\" ");
				} catch (NumberFormatException e) {
					e.printStackTrace();
				}
			}
		} else if ("update".equals(getType(metadata))) {
			if (null != parameterMap) {
				map.append(" parameterMap=\"").append(parameterMap).append(
						"\" ");
			} else {
				map.append(" parameterClass=\"").append(parameterClass).append(
						"\" ");
			}
			if (null != timeout) {
				try {
					Integer.parseInt(timeout);
					map.append(" timeout=\"").append(timeout).append("\" ");
				} catch (NumberFormatException e) {
					e.printStackTrace();
				}
			}
		} else if ("procedure".equals(getType(metadata))) {
			if (null != parameterMap) {
				map.append(" parameterMap=\"").append(parameterMap).append(
						"\" ");
			} else {
				map.append(" parameterClass=\"").append(parameterClass).append(
						"\" ");
			}
			if (null != resultMap) {
				map.append(" resultMap=\"").append(resultMap).append("\" ");
			} else {
				map.append(" resultClass=\"").append(resultClass).append("\" ");
				if (XML_TYPE.equals(resultClass) && null != xmlResultName) {
					map.append(" xmlResultName=\"").append(xmlResultName)
							.append("\" ");
				}
			}
			if (null != cacheModel) {
				map.append(" cacheModel=\"").append(cacheModel).append("\" ");
			}
			if (null != fetchSize) {
				try {
					Integer.parseInt(fetchSize);
					map.append(" fetchSize=\"").append(fetchSize).append("\" ");
				} catch (NumberFormatException e) {
					e.printStackTrace();
				}
			}
			if (null != remapResults) {
				if ("true".equals(remapResults.toLowerCase())
						|| "false".equals(remapResults.toLowerCase())) {
					map.append(" remapResults=\"").append(
							remapResults.toLowerCase()).append("\" ");
				}
			}
			// if(null!=resultSetType){
			// if("SCROLL_SENSITIVE".equals(resultSetType.toUpperCase())||"FORWARD_ONLY".equals(resultSetType.toUpperCase())||"SCROLL_INSENSITIVE".equals(resultSetType.toUpperCase())){
			// map.append("
			// resultSetType=\"").append(resultSetType.toUpperCase()).append("\"
			// ");
			// }
			// }
			if (null != timeout) {
				try {
					Integer.parseInt(timeout);
					map.append(" timeout=\"").append(timeout).append("\" ");
				} catch (NumberFormatException e) {
					e.printStackTrace();
				}
			}
		} else if ("statement".equals(getType(metadata))) {
			if (null != parameterMap) {
				map.append(" parameterMap=\"").append(parameterMap).append(
						"\" ");
			} else {
				map.append(" parameterClass=\"").append(parameterClass).append(
						"\" ");
			}
			if (null != resultMap) {
				map.append(" resultMap=\"").append(resultMap).append("\" ");
			} else {
				map.append(" resultClass=\"").append(resultClass).append("\" ");
				if (XML_TYPE.equals(resultClass) && null != xmlResultName) {
					map.append(" xmlResultName=\"").append(xmlResultName)
							.append("\" ");
				}
			}
			if (null != cacheModel) {
				map.append(" cacheModel=\"").append(cacheModel).append("\" ");
			}
			if (null != fetchSize) {
				try {
					Integer.parseInt(fetchSize);
					map.append(" fetchSize=\"").append(fetchSize).append("\" ");
				} catch (NumberFormatException e) {
					e.printStackTrace();
				}
			}
			if (null != remapResults) {
				if ("true".equals(remapResults.toLowerCase())
						|| "false".equals(remapResults.toLowerCase())) {
					map.append(" remapResults=\"").append(
							remapResults.toLowerCase()).append("\" ");
				}
			}
			if (null != resultSetType) {
				if ("SCROLL_SENSITIVE".equals(resultSetType.toUpperCase())
						|| "FORWARD_ONLY".equals(resultSetType.toUpperCase())
						|| "SCROLL_INSENSITIVE".equals(resultSetType
								.toUpperCase())) {
					map.append(" resultSetType=\"").append(
							resultSetType.toUpperCase()).append("\" ");
				}
			}
			if (null != timeout) {
				try {
					Integer.parseInt(timeout);
					map.append(" timeout=\"").append(timeout).append("\" ");
				} catch (NumberFormatException e) {
					e.printStackTrace();
				}
			}
		}
		return map.toString();
	}

	public String getSql(Object metadata) {
		JavaMethod javaMethod = (JavaMethod) metadata;
		String sql = javaMethod.getTagByName(IbatisSqlTagImpl.NAME).getValue();
		if (null == sql || "".equals(sql.trim())) {
			throw new RuntimeException("sql must be setting!");
		}
		String[] strs = sql.split("\\n");
		StringBuffer temp = new StringBuffer("");
		for (String str : strs) {
			if (!"".equals(str.trim())) {
				temp.append("\t\t").append(str).append("\n");
			}
		}
		return temp.toString();
	}

	public String getAlias(Object metadata) {
		JavaClass javaClass = (JavaClass) metadata;
		DocletTag[] tags = javaClass.getTagsByName(IbatisTypeAliasTagImpl.NAME);
		StringBuffer buffer = new StringBuffer("");
		for (DocletTag tag : tags) {
			String alias = tag.getNamedParameter("alias");
			String type = tag.getNamedParameter("type");
			if (null != alias && !"".equals(alias.trim()) && null != type
					&& !"".equals(type.trim())) {
				buffer.append("<typeAlias alias=\"").append(alias.trim())
						.append("\" ").append("type=\"").append(type.trim())
						.append("\"/>").append("\n");
			}
		}

		return buffer.toString();
	}

	public String getType(Object metadata) {
		String type = getMapTagNamedParameter(metadata, "type", "statement");
		if (!"select".equals(type) && !"insert".equals(type)
				&& !"update".equals(type) && !"delete".equals(type)
				&& !"procedure".equals(type) && !"statement".equals(type)) {
			type = "statement";
		}
		return type;
	}

	public String getResultMap(Object metadata) {
		String id = getResultMapTagNamedParameter(metadata, "id",
				((JavaMethod) metadata).getName());
		String _class = getMapTagNamedParameter(metadata, "class", DEFAULT_TYPE);
		String _extends = getMapTagNamedParameter(metadata, "extends", null);
		String groupBy = getMapTagNamedParameter(metadata, "group-by", null);
		String xmlName = getMapTagNamedParameter(metadata, "xml-name", null);

		StringBuffer map = new StringBuffer("");
		map.append("<resultMap id=\"").append(id).append("\" ").append(
				"class=\"").append(_class).append("\" ");
		if (null != _extends) {
			map.append(" extends=\"").append(_extends).append("\" ");
		}
		if (null != groupBy) {
			map.append(" groupBy=\"").append(groupBy).append("\" ");
		}
		if (null != xmlName) {
			map.append(" xmlName=\"").append(xmlName).append("\" ");
		}
		map.append(">\n");
		if (null != ((JavaMethod) metadata)
				.getTagByName(IbatisDiscriminatorTagImpl.NAME)) {
			if (null != ((JavaMethod) metadata)
					.getTagsByName(IbatisSubMapTagImpl.NAME)) {
				String javaType = getDiscriminatorTagNamedParameter(metadata,
						"java-type", OBJECT_TYPE);
				String column = getDiscriminatorTagNamedParameter(metadata,
						"column", null);
				String columnIndex = getDiscriminatorTagNamedParameter(
						metadata, "column-index", null);
				String jdbcType = getDiscriminatorTagNamedParameter(metadata,
						"jdbc-type", null);
				String nullValue = getDiscriminatorTagNamedParameter(metadata,
						"null-value", null);
				String typeHandler = getDiscriminatorTagNamedParameter(
						metadata, "type-handler", null);
				map.append("\t<discriminator javaType=\"").append(javaType)
						.append("\" ");
				if (null != column) {
					map.append(" column=\"").append(column).append("\" ");
				}
				if (null != columnIndex) {
					try {
						Integer.parseInt(columnIndex);
						map.append(" columnIndex=\"").append(columnIndex)
								.append("\" ");
					} catch (NumberFormatException e) {
						e.printStackTrace();
					}
				}
				if (null != jdbcType) {
					map.append(" jdbcType=\"").append(jdbcType).append("\" ");
				}
				if (null != nullValue) {
					map.append(" nullValue=\"").append(nullValue).append("\" ");
				}
				if (null != typeHandler) {
					map.append(" typeHandler=\"").append(typeHandler).append(
							"\" ");
				}
				map.append(">\n");
				DocletTag[] tags = ((JavaMethod) metadata)
						.getTagsByName(IbatisSubMapTagImpl.NAME);
				for (DocletTag tag : tags) {
					String resultMap = getTagNamedParameter(tag, "result-map",
							null);
					String value = getTagNamedParameter(tag, "value", null);
					if (null != resultMap && null != value) {
						map.append("\t\t<subMap resultMap=\"")
								.append(resultMap).append("\" value=\"")
								.append(value).append("\" ").append("/>\n");
					}
				}
				map.append("\t</discriminator>\n");
			}
		} else if (null != ((JavaMethod) metadata)
				.getTagByName(IbatisResultTagImpl.NAME)) {
			DocletTag[] tags = ((JavaMethod) metadata)
					.getTagsByName(IbatisResultTagImpl.NAME);
			for (DocletTag tag : tags) {
				String property = getTagNamedParameter(tag, "property", null);
				String javaType = getTagNamedParameter(tag, "java-type",
						OBJECT_TYPE);
				String column = getTagNamedParameter(tag, "column", null);
				String columnIndex = getTagNamedParameter(tag, "column-index",
						null);
				String jdbcType = getTagNamedParameter(tag, "jdbc-type", null);
				String nullValue = getTagNamedParameter(tag, "null-value", null);
				String typeHandler = getTagNamedParameter(tag, "type-handler",
						null);
				String resultMap = getTagNamedParameter(tag, "result-map", null);
				String select = getTagNamedParameter(tag, "select", null);
				if (null != property) {
					map.append("\t<result property=\"").append(property)
							.append("\" javaType=\"").append(javaType).append(
									"\" ");
					if (null != column) {
						map.append(" column=\"").append(column).append("\" ");
					}
					if (null != columnIndex) {
						try {
							Integer.parseInt(columnIndex);
							map.append(" columnIndex=\"").append(columnIndex)
									.append("\" ");
						} catch (NumberFormatException e) {
							e.printStackTrace();
						}
					}
					if (null != jdbcType) {
						map.append(" jdbcType=\"").append(jdbcType).append(
								"\" ");
					}
					if (null != nullValue) {
						map.append(" nullValue=\"").append(nullValue).append(
								"\" ");
					}
					if (null != typeHandler) {
						map.append(" typeHandler=\"").append(typeHandler)
								.append("\" ");
					}
					if (null != resultMap) {
						map.append(" resultMap=\"").append(resultMap).append(
								"\" ");
					}
					if (null != select) {
						map.append(" select=\"").append(select).append("\" ");
					}
					map.append("/>\n");
				}
			}

		}
		map.append("</resultMap>\n");
		return map.toString();
	}

	private String getMapTagNamedParameter(Object metadata, String name,
			String value) {
		JavaMethod javaMethod = (JavaMethod) metadata;
		String str = javaMethod.getNamedParameter(IbatisMapTagImpl.NAME, name);
		return null == str ? value : str.trim();
	}

	private String getResultMapTagNamedParameter(Object metadata, String name,
			String value) {
		JavaMethod javaMethod = (JavaMethod) metadata;
		String str = javaMethod.getNamedParameter(IbatisResultMapTagImpl.NAME,
				name);
		return null == str ? value : str.trim();
	}

	private String getResultTagNamedParameter(Object metadata, String name,
			String value) {
		JavaMethod javaMethod = (JavaMethod) metadata;
		String str = javaMethod.getNamedParameter(IbatisResultTagImpl.NAME,
				name);
		return null == str ? value : str.trim();
	}

	private String getTagNamedParameter(DocletTag tag, String name, String value) {
		String str = tag.getNamedParameter(name);
		return null == str ? value : str.trim();
	}

	private String getSubMapTagNamedParameter(Object metadata, String name,
			String value) {
		JavaMethod javaMethod = (JavaMethod) metadata;
		String str = javaMethod.getNamedParameter(IbatisSubMapTagImpl.NAME,
				name);
		return null == str ? value : str.trim();
	}

	private String getDiscriminatorTagNamedParameter(Object metadata,
			String name, String value) {
		JavaMethod javaMethod = (JavaMethod) metadata;
		String str = javaMethod.getNamedParameter(
				IbatisDiscriminatorTagImpl.NAME, name);
		return null == str ? value : str.trim();
	}

	public boolean shouldGenerate(Object metadata) {
		JavaClass javaClass = (JavaClass) metadata;
		return javaClass.getTagByName(IbatisConfigTagImpl.NAME) == null ? false
				: true;
	}
}
